! module *dynamic_quantities*
!
! Purpose:
!   (1) create memory block for storing quantities that 
!       are related to dynamics process
!   (2) update dynamic quantities
!   (3) input/output dynamic quantities data
!
! Author:
!   Name: DONG Li
!   Email: dongli@lasg.iap.ac.cn

module dynamic_quantities

    use common_info
    use space_manager
    use time_manager
    use shock_daemon
    use message
    use string
    use file_io
    use run_manager

    implicit none

    integer :: num_parcel
    integer :: num_quantity
    real, allocatable :: location(:,:)
    real, allocatable, target :: quantities(:,:)

    character(50) :: module_name = "dynamic_quantities"
    character(8) :: quantities_name(10)
    character(8) :: coordinate_name(3)

    logical, private :: initialized = .false.

    namelist /dynamic_quantities_parameters/ &
        num_parcel, num_quantity, quantities_name, &
        coordinate_name

contains

    subroutine dynamic_quantities_init(namelist_file)
        character(*), intent(in) :: namelist_file

        integer :: i
        character(100) :: str, msg

        ! module dependencies
        if(.not. common_info_is_initialized()) then
            call message_show(ERROR_MSG,    &
                "dynamic_quantities",       &
                "dynamic_quantities_init",  &
                """dynamic_quantities"" "   &
                // "needs ""common_info"" " &
                // "to be initialized first")
            call run_manager_end_run
        end if

        ! read in namelist
        open(10, file=namelist_file)
        read(10, nml=dynamic_quantities_parameters)
        close(10)

        if(num_quantity > 10) then
            call string_from_integer(num_quantity, str)
            msg = "num_quantitiy (" &
                // trim(str)        &
                // ") should not be greater than 10"
            call message_show(ERROR_MSG,   &
                "dynamic_quantities",      &
                "dynamic_quantities_init", &
                msg)
            call run_manager_end_run
        end if

        allocate(location(num_dim,num_parcel))
        allocate(quantities(num_parcel,num_quantity))

        call file_io_init

        initialized = .true.

        return
    end subroutine dynamic_quantities_init

    logical function dynamic_quantities_is_initialized &
        result(res)
        res = initialized
    end function dynamic_quantities_is_initialized

    subroutine dynamic_quantities_input
        integer :: i

        call file_io_create_input_file(input_file)
        do i = 1, num_dim
            call file_io_input(input_file, module_name, &
                coordinate_name(i), location(i,:),      &
                1, (/num_parcel/))
        end do
        do i = 1, num_quantity
            call file_io_input(input_file, module_name, &
                quantities_name(i), quantities(:,i),    &
                1, (/num_parcel/))
        end do

        ! output initial data
        call dynamic_quantities_output

        return
    end subroutine dynamic_quantities_input

    subroutine dynamic_quantities_output
        integer :: i
        character(20) :: label

        if(time_manager_is_first_step()) then
            call file_io_create_output_file(output_file)
            call file_io_create_data_folder(output_file, &
                module_name)
            do i = 1, num_dim
                call file_io_create_data_card(output_file, &
                    module_name, "parcel_"//trim(coordinate_name(i)))
            end do
            do i = 1, num_quantity
                call file_io_create_data_card(output_file, &
                    module_name, "parcel_"//trim(quantities_name(i)))
            end do
        end if

        label = time_manager_cur_time_string()
        do i = 1, num_dim
            call file_io_output(trim(label), output_file,         &
                module_name, "parcel_"//trim(coordinate_name(i)), &
                location(i,:), 1, (/num_parcel/))
        end do
        do i = 1, num_quantity
            call file_io_output(trim(label), output_file,         &
                module_name, "parcel_"//trim(quantities_name(i)), &
                quantities(:,i), 1, (/num_parcel/))
        end do

        return
    end subroutine dynamic_quantities_output

    integer function dynamic_quantities_get_index(quantity_name) &
        result(index)
        character(*), intent(in) :: quantity_name

        integer :: i
        logical :: flag
        character(100) :: msg

        flag = .false.
        do i = 1, num_quantity
            if(trim(quantity_name) == trim(quantities_name(i))) then
                index = i
                flag = .true.
                exit
            end if
        end do

        if(flag == .false.) then
            msg = "quantity """ // trim(quantity_name) &
                // """ is invalid"
            call message_show(ERROR_MSG,        &
                "dynamic_quantities",           &
                "dynamic_quantities_get_index", &
                msg)
            call run_manager_end_run    
        end if

        return
    end function dynamic_quantities_get_index

    subroutine dynamic_quantities_get_pointer(qi, ptr)
        integer, intent(in) :: qi
        real, intent(out), pointer :: ptr(:)

        ptr => quantities(:,qi)

        return
    end subroutine dynamic_quantities_get_pointer

    subroutine dynamic_quantities_parcel_move(u_index)
        integer, intent(in) :: u_index(num_dim)

        integer :: i, j

        !call shock_daemon_detect_mix(num_parcel, location, quantities(:,u_index))

        do j = 1, num_parcel
            do i = 1, num_dim
                location(i,j) = location(i,j)+ &
                    quantities(j,u_index(i))*dt
                ! boundary condition
                select case(boundary_type(i))
                case("periodic")
                    if(location(i,j) < boundary(1,i)) then
                        location(i,j) = location(i,j)+ &
                            (boundary(2,i)-boundary(1,i))
                    else if(location(i,j) > boundary(2,i)) then
                        location(i,j) = location(i,j)- &
                            (boundary(2,i)-boundary(1,i))
                    end if
                end select
            end do
        end do

        return
    end subroutine dynamic_quantities_parcel_move

    subroutine dynamic_quantities_update(qi, tendency)
        integer, intent(in) :: qi
        real, intent(in) :: tendency(num_parcel)

        quantities(:,qi) = quantities(:,qi)+tendency*dt

        return
    end subroutine dynamic_quantities_update

end module dynamic_quantities
